const { src, dest, series, parallel, watch } = require('gulp')
const sass = require('gulp-sass')
const babel = require('gulp-babel') // gulp只是一个插件，我们还需要对应的模块@babel/core @babel/preset-env
const swig = require('gulp-swig')
const imagemin = require('gulp-imagemin')
const useref = require('gulp-useref')
const gif = require('gulp-if')
const htmlmin = require('gulp-htmlmin')
const uglify = require('gulp-uglify')
const cleanCss = require('gulp-clean-css')
const del = require('del')
const browserSync = require('browser-sync')

let conf = {
  src: 'src',
  dist: 'dist',
  public: 'public',
  temp: '.temp',
  paths: {
    styles: 'assets/styles/*.scss',
    scripts: 'assets/scripts/*.js',
    pages: '*.html',
    images: 'assets/images/**',
    fonts: 'assets/fonts/**',
    extra: 'public/**'
  },
  tmplEngiData: {
    menus: [
      {
        name: 'Home',
        icon: 'aperture',
        link: 'index.html'
      },
      {
        name: 'Features',
        link: 'features.html'
      },
      {
        name: 'About',
        link: 'about.html'
      },
      {
        name: 'Contact',
        link: '#',
        children: [
          {
            name: 'Twitter',
            link: 'https://twitter.com/w_zce'
          },
          {
            name: 'About',
            link: 'https://weibo.com/zceme'
          },
          {
            name: 'divider'
          },
          {
            name: 'About',
            link: 'https://github.com/zce'
          }
        ]
      }
    ],
    pkg: require('./package.json'),
    date: new Date()
  },
  devServe: {
    notify: false,
    port: '8080',
    open: true
  }
}

// 变量定义
const srcOptions = { base: conf.src, cwd: conf.src }
const compressSrcOptions = { base: conf.temp, cwd: conf.temp }
const babelOptions = { presets: ['@babel/preset-env'] }
const swigOptions = { defaults: { cache: false }, data: conf.tmplEngiData }
// 通过create创建一个web服务
const bs = browserSync.create()

// 文件清除
const clean = () => {
  return del([conf.temp, conf.dist])
}

// 对源文件的加工动作
// 编译处理
// 样式处理任务
const styleHandle = () => {
  return src(conf.paths.styles, srcOptions)
    .pipe(sass()) // 转化操作
    .pipe(dest(conf.temp)) // 转化后的文件先存储到临时文件夹内，为了压缩这些文件至真正的项目中
    .pipe(bs.reload({ stream: true }))
}

// 脚本处理任务
const scriptHandle = () => {
  return src(conf.paths.scripts, srcOptions)
    .pipe(babel(babelOptions))
    .pipe(dest(conf.temp))
    .pipe(bs.reload({ stream: true }))
}

// 页面布局处理任务
const pageHandle = () => {
  return src(conf.paths.pages, srcOptions)
    .pipe(swig(swigOptions))
    .pipe(dest(conf.temp))
    .pipe(bs.reload({ stream: true }))
}

// 将这类编译处理任务合并成并列任务
const transfer = parallel(styleHandle, scriptHandle, pageHandle)

// 图片和字体图标的压缩
// 图片操作
const imageHandle = () => {
  return src(conf.paths.images, srcOptions)
    .pipe(imagemin())
    .pipe(dest(conf.dist)) // 直接输出到目标目录中
}
// 字体图标操作
const fontHandle = () => {
  return src(conf.paths.fonts, srcOptions)
    .pipe(imagemin())
    .pipe(dest(conf.dist)) // 直接输出到目标目录中
}
// 特殊操作
const extraHandle = () => {
  return src(conf.paths.extra, srcOptions)
    .pipe(dest(conf.dist))
}

// 启动web服务任务
const serve = () => {
  // 开启watch
  watch(conf.paths.styles, { cwd: conf.src }, styleHandle)
  watch(conf.paths.scripts, { cwd: conf.src }, scriptHandle)
  watch(conf.paths.pages, { cwd: conf.src }, pageHandle)
  watch([
    conf.paths.images,
    conf.paths.fonts,
  ], { cwd: conf.src }, bs.reload)
  watch('**', { cwd: conf.public }, bs.reload)

  const { devServe } = conf
  bs.init({
    notify: devServe.notify,
    open: devServe.open,
    port: devServe.port, // 为何不起作用，位置定义错误
    // files: 'temp/**', // files会监听指定路径下的文件变化并重启服务，那么files能不能指定src呢？不行，因为我们不仅需要重启还需要重编译
    server: {
      baseDir: [conf.temp, conf.src, conf.public], // 编译的目录
      routes: {
        '/node_modules': 'node_modules'
      }, // 在编译前会先将routes配置的路径进行替换
    }
  })
}

// 压缩html css js 文件写入输出目录下
const compress = () => {
  return src(conf.paths.pages, compressSrcOptions)
    .pipe(useref({ searchPath: [conf.temp, '.'] })) // searchPath的用处?
    .pipe(gif(/\.js$/, uglify())) // 通过gulp-if插件来判断当前文件的格式，从而来判断用什么插件进行压缩
    .pipe(gif(/\.css$/, cleanCss()))
    .pipe(gif(/\.html$/, htmlmin({
      collapseWhitespace: true,
      minifyJS: true,
      minifyCSS: true
    }))) // html的压缩需要进行一些配置选项
    .pipe(dest(conf.dist)) // 同一文件夹的读写有可能会导致压缩的失败，所以将压缩的文件放到临时文件夹内
}

// 将开发模式的任务合并起来
const dev = series(transfer, serve)

// 完整构建任务
const build = series(
  clean, // 先清空dist和temp目录
  parallel(
    series(transfer, compress), // 先将文件编译到temp，再将temp内的文件压缩到dist
    extraHandle,
    imageHandle,
    fontHandle))

// 导出任务
module.exports = {
  clean,
  dev,
  build
}